package org.minbox.framework.microservice.common.util;

import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTCreator;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTCreationException;
import com.auth0.jwt.exceptions.TokenExpiredException;
import com.auth0.jwt.interfaces.Claim;
import com.auth0.jwt.interfaces.DecodedJWT;

import lombok.extern.slf4j.Slf4j;
import org.minbox.framework.microservice.common.exception.AccessTokenExpiredException;
import org.minbox.framework.microservice.common.exception.AccessVerificationException;

import java.util.Calendar;
import java.util.Collections;
import java.util.Map;

/**
 * JWT工具类
 *
 * @author XiaoQingqing
 * @since 2024/03/06
 */
@Slf4j
public class JwtUtil {
    /**
     * 默认TOKEN体键
     */
    public static final String DEFAULT_TOKEN_BODY_KEY = "Authorization";
    /**
     * 默认Token密钥
     */
    private static final String DEFAULT_TOKEN_SECRET = "wttDs1auyHtSSl1Q";
    /**
     * 以默认参数编码
     *
     * @param value        值
     * @param delaySeconds 延长时间
     * @return JWT TOKEN
     */
    public static String encodeDefault(String value, int delaySeconds){
        return encode(DEFAULT_TOKEN_BODY_KEY, value, delaySeconds, DEFAULT_TOKEN_SECRET);
    }

    /**
     * 编码
     *
     * @param key          键
     * @param value        值
     * @param delaySeconds 延长时间
     * @param secret       密钥
     * @return JWT TOKEN
     */
    public static String encode(String key, String value, int delaySeconds, String secret){
        Map<String, String> claims = Collections.singletonMap(key, value);
        return encode(claims, delaySeconds, secret);
    }

    /**
     * 编码
     *
     * @param claims       Token体
     * @param delaySeconds 延长时间
     * @param secret       密钥
     * @return JWT TOKEN
     */
    public static String encode(Map<String, String> claims, int delaySeconds, String secret) {
        return encode(Collections.emptyMap(), claims, delaySeconds, secret);
    }

    /**
     * 编码
     *
     * @param header       Token头
     * @param claims       Token体
     * @param delaySeconds 延长时间
     * @param secret       密钥
     * @return JWT TOKEN
     */
    public static String encode(Map<String, Object> header, Map<String, String> claims, int delaySeconds, String secret) {
        Algorithm algorithm = Algorithm.HMAC256(secret);
        Calendar calendar = Calendar.getInstance();
        calendar.add(Calendar.SECOND, delaySeconds);
        JWTCreator.Builder builder = JWT.create()
                .withHeader(header)
                .withExpiresAt(calendar.getTime());
        for (String key : claims.keySet()) {
            builder.withClaim(key, claims.get(key));
        }
        try {
            return builder.sign(algorithm);
        } catch (JWTCreationException ex) {
            log.error("编码构建Token异常", ex);
            throw new AccessVerificationException();
        }
    }

    /**
     * 以默认参数解码
     *
     * @param token  令牌
     * @param clazz  值所属类
     * @return 值
     * @param <T> 值类型
     */
    public static <T> T decodeDefault(String token, Class<T> clazz) {
        return decode(token, DEFAULT_TOKEN_SECRET, DEFAULT_TOKEN_BODY_KEY, clazz);
    }

    /**
     * 解码
     *
     * @param token  令牌
     * @param secret 密钥
     * @param key    键
     * @param clazz  值所属类
     * @return 值
     * @param <T> 值类型
     */
    public static <T> T decode(String token, String secret, String key, Class<T> clazz) {
        DecodedJWT jwt = decode(token, secret);
        Claim claim = jwt.getClaim(key);
        return null == claim ? null : claim.as(clazz);
    }

    /**
     * 解码
     *
     * @param token  令牌
     * @param secret 密钥
     * @return 解码对象
     */
    public static DecodedJWT decode(String token, String secret) {
        Algorithm algorithm = Algorithm.HMAC256(secret);
        JWTVerifier verifier = JWT.require(algorithm).build();
        try {
            return verifier.verify(token);
        } catch (TokenExpiredException ex){
            log.error("解码Token异常，Token过期", ex);
            throw new AccessTokenExpiredException();
        } catch (JWTCreationException ex){
            log.error("解码Token异常，Token无效", ex);
            throw new AccessVerificationException();
        }

    }
}
